package xj.graph2d.shapes.uml;

import static xj.graph2d.DrawShapeConstants.cpady;

import java.util.HashMap;
import java.util.Map;

import xj.graph2d.DrawShape;
import xj.graph2d.DrawShapeBase;
import xj.graph2d.Label;
import xj.graph2d.shapes.CompartmentalRoundRectangleShape;
import xj.graph2d.shapes.block.BlockShape;
import xj.meta.ModelElement;
import xj.model.uml.State;

public class SimpleStateShape 
  extends CompartmentalRoundRectangleShape
  implements StateShape {

  public SimpleStateShape() {
    super();
    adapter.setNumberOfHorizontalCompartmentsFixed(true);
    adapter.setScaleTopCompartment(false);
  }

  public SimpleStateShape(boolean createComp) {
    super(createComp);
    adapter.setNumberOfHorizontalCompartmentsFixed(true);
    adapter.setScaleTopCompartment(false);
  }

  public SimpleStateShape(float[] dividers, boolean filled, boolean createComp) {
    super(dividers, filled, createComp);
    adapter.setNumberOfHorizontalCompartmentsFixed(true);
    adapter.setScaleTopCompartment(false);
    if (dividers != null && dividers.length > 1) {
      actionCompartmentShown = true;
    } else {
      actionCompartmentShown = false;
    }
  }

  public SimpleStateShape(float x1, float y1, float x2, float y2) {
    this(x1, y1, x2, y2, null);
  }

  public SimpleStateShape(float x1, float y1, float x2, float y2,
			  float[] dividers) {
    super(x1, y1, x2, y2, dividers);
    adapter.setNumberOfHorizontalCompartmentsFixed(true);
    adapter.setScaleTopCompartment(false);
    if (dividers != null && dividers.length > 0) {
      actionCompartmentShown = true;
    } else {
      actionCompartmentShown = false;
    }
  }

  @Override
  public DrawShape makeInstance() {
    return new SimpleStateShape(null, filled, comp != null);
  }

  @Override
  public void copyTo(DrawShapeBase s) 
    throws CloneNotSupportedException {
    super.copyTo(s);
    if (s instanceof SimpleStateShape) {
      SimpleStateShape ss = (SimpleStateShape) s;
      ss.actionCompartmentShown = actionCompartmentShown;
    }
  }

  @Override
  protected void cloneSubshapes(DrawShapeBase s, boolean deepClone) 
    throws CloneNotSupportedException {
    super.cloneSubshapes(s, deepClone);
    if (s instanceof SimpleStateShape) {
      SimpleStateShape ss = (SimpleStateShape) s;
      ss.actionBox = ss.getShapeInCompartment(1);
      if (ss.actionBox != null) { 
	ss.actionBox.setVisible(ss.actionCompartmentShown);
      }
    }
  }

  @Override
  public String getNodeDisplayName() { 
    return getStateName();
  }

  public String getStateName() {
    return UMLUtil.getName(this);
  }

  public void setStateName(String cname) {
    UMLUtil.setName(this, cname);
    doLayout();
  }

  public String getStateStereotypes() {
    return UMLUtil.getStereotypes(this);
  }

  public void setStateStereotypes(String st) {
    UMLUtil.setStereotypes(this, st);
  }

  @Override
  public boolean isAcceptSubshape(DrawShape subshape) {
    if (subshape != null) {
      ModelElement m1 = getModel();
      ModelElement m2 = subshape.getModel();
      if (m1 instanceof State && m2 instanceof State) {
	State s1 = (State) m1;
	State s2 = (State) m2;
	if (s1.getType() == State.StateType.Submachine_State) {
	  State.StateType t = s2.getType();
	  if (t == State.StateType.Entry_Point || 
	      t == State.StateType.Exit_Point) {
	    return true;
	  } else {
	    return false;
	  }
	}
      }
    }
    return acceptSubshape && (subshape != null);
  }

  // shape --> model
  public void getActions(State state) {
    if (state != null && 
	actionCompartmentShown) {
      DrawShape box = getActionBox();
      UMLStateUtil.getActions(state, box);
    }
  }

  // model --> shape
  public void setActions(State state) {
    if (state != null && 
	actionCompartmentShown) {
      DrawShape box = getActionBox();
      DrawShape newbox = UMLStateUtil.setActions(state, box);
      setActionBox(newbox);
      actionBox = newbox;
      fitCompartments();
      doLayout();
    }
  }

  public void initStateHeader(Label label, Label stereotype) {
    UMLUtil.initHeader(this, label, stereotype);
  }

  public void initAction(DrawShape action) {
    actionBox = action;
    if (action != null) {
      action.setDisplayName("Action");
      setShapeInCompartment(action, 1);
      actionBox.setVisible(actionCompartmentShown);
    }
    doLayout();
  }

  public boolean isModernStyle() { 
    return (actionBox != null && actionBox instanceof BlockShape);
  }

  public void changeStyle(DrawShape action) { 
    if (actionBox != null && action != null) {
      ModelElement model = getModel();
      if (model instanceof State) {
	State state = (State) model;
	initAction(action);
	setActions(state);
      }
    }
  }

  public boolean isActionCompartmentShown() {
    return actionCompartmentShown;
  }

  public void showActionCompartment() {
    if (!actionCompartmentShown) {
      actionCompartmentShown = true;

      // necessary to sync with model, which may have been 
      // edited when the action box is hidden
      ModelElement model = getModel();
      if (model instanceof State) {
	State state = (State) model;
	setActions(state);
      }

      float[] dividers;
      float h;
      float h0 = getShapeHeight();
      if (actionBox != null) {
	actionBox.setVisible(true);
	float h1 = actionBox.getShapeHeight() + 2 * cpady;
	h = (h0 + h1);
	dividers = new float[] { h0 / h };
	moveEnd2(0, h1);
      } else {
	dividers = new float[] { 0.4f };
	moveEnd2(0, h0 * 2.5f);
      }
      setHorizontalDividers(dividers);

      if (actionBox != null) {
	setShapeInCompartment(actionBox, 1);
      }
      arrangeSubshapes();
      fitCompartments();
      doLayout();
    }
  }

  public void hideActionCompartment() {
    actionBox = getShapeInCompartment(1);
    if (actionBox != null) {
      actionBox.setVisible(false);

      float h = getShapeHeight();
      float dy = 0;
      float[] dividers = getHorizontalDividers();
      if (dividers != null) {
	dy = -h * (1 - dividers[0]);
      }
      setHorizontalDividers(null);
      moveEnd2(0, dy);
      doLayout();
    }
    actionCompartmentShown = false;
  }

  @Override
  public String toString() { 
    return "SimpleStateShape " + getStateName();
  }

  @Override
  protected Map<String, Object> getXMLAttributes() {
    Map<String, Object> attrs = super.getXMLAttributes();
    if (attrs == null) {
      attrs = new HashMap<String, Object>();
    }

    attrs.put("showAction", actionCompartmentShown);
    return attrs;
  }

  @Override
  protected void processXMLAttributes(org.w3c.dom.Element node) {
    if (node != null) {
      super.processXMLAttributes(node);

      String s;
      s = node.getAttribute("showAction");
      actionCompartmentShown = Boolean.valueOf(s);

      if (actionBox != null) { 
	actionBox.setVisible(actionCompartmentShown);
      }
      /*
      if (!actionCompartmentShown) {
	actionBox.setVisible(false);
      }
      */
    }
  }

  public void arrangeSubshapes() {
    actionBox = getShapeInCompartment(1);
  }

  public DrawShape getActionBox() {
    if (actionCompartmentShown) {
      return getShapeInCompartment(1);
    }
    return null;
  }

  public void setActionBox(DrawShape action) {
    if (action != null && 
	actionCompartmentShown) {
      setShapeInCompartment(action, 1);
    }
  }

  protected boolean actionCompartmentShown;

  protected DrawShape actionBox;

}
